/*
    多线程共享进程的地址空间，如果多个线程需要访问同一块内存，用全局变量就可以了。

    但是，在多进程中，每个进程的地址空间是独立的，不共享的，如果多个进程需要访问同一块内存，不能
用全局变量，只能用共享内存。
    
    共享内存(Shared Memory)允许多个进程(不要求进程之间有血缘关系)访问同一块内存空间
是多个进程之间共享和传递数据最高效的方式。进程可以将共享内存连接到它们自己的地址空间中，如
果某个进程修改了共享内存中的数据，其它的进程读到的数据也将会改变。

    共享内存并未提供锁机制，也就是说，在某一个进程对共享内存的进行读/写的时候，不会阻止其它
程对它的读/写。如果要对共享内存的读/写加锁，可以使用信号量。

    Linux 中提供了一组函数用于操作共享内存。



一、shmget函数

该函数用于创建/获取共享内存。

函数声明:
    int shmget(key_t key, size_t size, int shmflg);

        key:    共享内存的键值，是一个整数(typedef unsigned int key_t)一般采用十六进制，例如
0x5005，不同共享内存的 key不能相同。

        size:   待创建的共享内存的大小，以字节为单位。

        shmflg: 共享内存的访问权限，与文件的权限一样，例如 0666|IPC CREAT，表示全部用户对它可读
写，IPC CREAT 表示如果共享内存不存在，就创建它。

        返回值:成功返回共享内存的id(一个大于0的整数,shmid)，失败返回-1(系统内存不足 或 没有访问权限)。

shell命令: ipcs -m  可以查看系统的共享内存
共享内存一般由程序创建和删除，也可以用shell命令 ipcrm -m +共享内存编号(shmid)







二、shmat函数

该函数用于把共享内存连接到当前进程的地址空间。若想使用共享内存，则必须调用该函数

函数声明:
    void *shmat(int shmid, const void *shmaddr, int shmflg);

        shmid:      由 shmget()函数返回的共享内存标识。

        shmaddr:    指定共享内存连接到当前进程中的地址位置，通常填0，表示让系统来选择
共享内存的地址。

        shmflg:     标志位，通常填 0。

    调用成功时返回共享内存起始地址，失败返回(void*)-1.







三、shmdt 函数

该函数用于将共享内存从当前进程中分离，相当于shmat()函数的反操作。

函数声明:
    int shmdt(const void *shmaddr);
        shmaddr:    shmat()函数返回的地址。即共享内存其实地址

    调用成功时返回0，失败时返回-1。






四、shmctl 函数

该函数用于操作共享内存，最常用的操作是删除共享内存。

函数声明:
    int shmctl(int shmid, int command, struct shmid ds *buf);
        
        shmid:      shmget()函数返回的共享内存id。
       
        command:    操作共享内存的指令，如果要删除共享内存，填IPC_RMID。

        buf:        操作共享内存的数据结构的地址，如果要删除共享内存，填 0。

    调用成功时返回0，失败时返回-1。

注意: 
    1)用 root 创建的共享内存，不管创建的权限是什么，普通用户无法删除。

    2)共享内存的数据结构可以用结构体也可以用数组，但是只能用C++内置数据类型，不能用STL容器
原因是STL容器会动态的在堆区分配内存，堆区的内存不属于共享内存，会出现段错误

*/





#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/shm.h>
using namespace std;

struct People           //结构体
{
    int age;            //年龄
    char name[5];       //姓名
};

int main(int argc, char *argv[])
{
    //if(argc!=3) { cout << "Using: ./demo12 no name \n"; return -1;}
    
    //第1步，创建/获取共享内存，键值key为0x5005,也可以用其他的值。
    int shmid = shmget(0x5005, sizeof(People), 0640|IPC_CREAT);
    if( shmid == -1)
    {   
        cout << "shmget(0x5005) failed." << endl;
        return -1;
    }

    cout << "shmid = " << shmid << endl;
    int shmid = shmget(0x5005, sizeof(People), 0640|IPC_CREAT);
    if( shmid == -1)
    {
        cout << "shmget(0x5005) failed." << endl;
        return -1;
    }

    cout << "shmid = " << shmid << endl;

    //第2步: 把共享内存连接到当前进程的地址空间
    People *ptr = (People *)shmat(shmid, 0, 0);         //共享内存就是内存，没有数据类型，所以需要强制类型转换
    if( ptr == (void*)-1)
    {
        cout << "shmat() failed." << endl;
        return -1;
    }

    //第3步: 使用共享内存，对共享内存进行读/写
    cout << "原值: age = " << ptr->age << ",  name = " << ptr->name << endl;
    ptr->age = atoi(argv[1]);
    strcpy(ptr->name, argv[2]);
    cout << "新值: age = " << ptr->age << ",  name = " << ptr->name << endl;

    //第4步: 把共享内存从当前进程中分离
    shmdt(ptr);

    //第5步: 删除共享内存
    if(shmctl(shmid, IPC_RMID, 0) == -1)
    {
        cout << "shmctl failed." << endl;
        return -1;
    }


    return 0;
}












